package com.xiaoyu.lanling.feature.voicematch.activity

import `in`.srain.cube.app.lifecycle.AppActivityLifecycleCallbacks
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.media.MediaPlayer
import android.os.Bundle
import android.os.PowerManager
import android.view.View
import com.xiaoyu.base.app.App
import com.xiaoyu.base.app.AppContext
import com.xiaoyu.base.app.GlobalUI
import com.xiaoyu.base.data.UserData
import com.xiaoyu.base.event.AppEventBus
import com.xiaoyu.base.event.ErrorMessageEvent
import com.xiaoyu.base.event.SimpleEventHandler
import com.xiaoyu.base.utils.extensions.setOnClickDebounceListener
import com.xiaoyu.lanling.R
import com.xiaoyu.lanling.activity.base.AppCompatToolbarActivity
import com.xiaoyu.lanling.activity.base.BaseDialogFragment
import com.xiaoyu.lanling.event.voicematch.*
import com.xiaoyu.lanling.feature.main.activity.MainActivity
import com.xiaoyu.lanling.feature.voicematch.VoiceCallFloatWindowManager
import com.xiaoyu.lanling.feature.voicematch.data.VoiceMatchData
import com.xiaoyu.lanling.feature.voicematch.data.VoiceMatchData.Companion.MATCH_STATUS_EXIT
import com.xiaoyu.lanling.feature.voicematch.data.VoiceMatchData.Companion.MATCH_STATUS_INIT
import com.xiaoyu.lanling.feature.voicematch.data.VoiceMatchData.Companion.MATCH_STATUS_MATCHED
import com.xiaoyu.lanling.feature.voicematch.data.VoiceMatchData.Companion.MATCH_STATUS_WAITING
import com.xiaoyu.lanling.feature.voicematch.log.VoiceMatchLogger
import com.xiaoyu.lanling.feature.voicematch.util.VoiceMatchNotificationUtils
import com.xiaoyu.lanling.lifecycle.disposeOn
import com.xiaoyu.lanling.media.image.ImageViewUtil
import com.xiaoyu.lanling.router.Router
import com.xiaoyu.lib_av.datamodel.CallParams
import com.xiaoyu.lib_av.state.CallStateManager
import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import kotlinx.android.synthetic.main.voice_match_waiting_activity.*
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import java.util.concurrent.TimeUnit

class VoiceMatchWaitingActivity : AppCompatToolbarActivity(), BaseDialogFragment.OnDismissListener {

    companion object {
        const val subtitleCount = 3
        var isShowing = false
        val maleSubtitleMap = mutableMapOf<Int, Int>().apply {
            put(0, R.string.voice_match_waiting_male_subtitle_0)
            put(1, R.string.voice_match_waiting_male_subtitle_1)
            put(2, R.string.voice_match_waiting_male_subtitle_2)
        }
        val femaleSubtitleMap = mutableMapOf<Int, Int>().apply {
            put(0, R.string.voice_match_waiting_female_subtitle_0)
            put(1, R.string.voice_match_waiting_female_subtitle_1)
            put(2, R.string.voice_match_waiting_female_subtitle_2)
        }
    }

    private val requestTag = Any()
    private var mSubtitleIndex: Int = 0

    private var mMainTaskTopActivity: Activity? = null

    private var mRingtoneMediaPlayer: MediaPlayer? = null
    private var mWaitStartTime = 0L
    private var mMatchStatus = MATCH_STATUS_INIT
    private var mRankDisposable: Disposable? = null
    private var mIsInQueueDisposable: Disposable? = null

    override fun onCreateSafelyAfterAppFinishInit(savedInstanceState: Bundle?) {
        super.onCreateSafelyAfterAppFinishInit(savedInstanceState)
        setContentView(R.layout.voice_match_waiting_activity)
        setDarkStatusBar()

        initBind()
        initEvent()
        initData()

        CallStateManager.queue()
    }

    override fun onStartSafelyAfterAppFinishInit(isFirstTimeStart: Boolean) {
        super.onStartSafelyAfterAppFinishInit(isFirstTimeStart)
        playRingtone()
    }

    override fun onResumeSafelyAfterAppFinishInit(isFirstTimeResume: Boolean) {
        super.onResumeSafelyAfterAppFinishInit(isFirstTimeResume)
        mMainTaskTopActivity = App.getInstance().topActivity
        VoiceCallFloatWindowManager.instance.hideAndStopFloatWindow()
    }

    override fun onStop() {
        super.onStop()
        VoiceCallFloatWindowManager.instance.showFloatWindow(this, -1, true, null)
        closeRingtone()
    }

    override fun onDestroy() {
        super.onDestroy()
        logEventForExitBeforeMatch()
        dispose()
        isShowing = false
        VoiceCallFloatWindowManager.instance.hideAndStopFloatWindow()
        CallStateManager.dequeue()
    }

    private fun logEventForExitBeforeMatch() {
        when (mMatchStatus) {
            MATCH_STATUS_INIT -> VoiceMatchLogger.exitBeforeMatch()
            MATCH_STATUS_WAITING -> exit("kill_app")
        }
    }

    override fun onBackPressed() {
        val topActivity = mMainTaskTopActivity
        val topIntent: Intent? = topActivity?.intent
        if (topActivity == null || topActivity is VoiceMatchWaitingActivity) {
            val intent = Intent(this, MainActivity::class.java)
            startActivity(intent)
        } else if (topIntent != null) {
            topIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP)
            startActivity(topIntent)
        }
    }

    private fun initBind() {
        back.setOnClickDebounceListener {
            back.isEnabled = false
            exit("back")
        }
        minimum_button.setOnClickDebounceListener {
            onBackPressed()
        }
    }

    private fun initEvent() {
        AppEventBus.bindContainerAndHandler(this, object : SimpleEventHandler() {
            @Subscribe(threadMode = ThreadMode.MAIN)
            fun onEvent(event: VoiceMatchExitMatchEvent) {
                if (event.isNotFromThisRequestTag(requestTag)) {
                    return
                }

                if (event.isFailed) {
                    VoiceMatchLogger.exitRequestFailure()
                } else {
                    VoiceMatchLogger.exitRequestSuccess()
                    mMatchStatus = MATCH_STATUS_EXIT
                    finishAndRemoveTask()
                }
            }

            @Subscribe(threadMode = ThreadMode.MAIN)
            fun onEvent(event: VoiceMatchReceiveEvent) {
                processCall(event.callParams)
                finishAndRemoveTask()
            }

            @Subscribe(threadMode = ThreadMode.MAIN)
            fun onEvent(event: VoiceMatchIsInQueueEvent) {
                if (event.isNotFromThisRequestTag(requestTag)) {
                    return
                }
                if (event.isInQueue) {
                    return
                }
                finishAndRemoveTask()
            }

            @Subscribe(threadMode = ThreadMode.MAIN)
            fun onEvent(event: ErrorMessageEvent) {
                if (event.isNotFromThisRequestTag(requestTag)) {
                    return
                }
                back.isEnabled = true
            }

            @Subscribe(threadMode = ThreadMode.MAIN)
            fun onEvent(event: VoiceMatchApplyMatchEvent) {
                if (event.isNotFromThisRequestTag(requestTag)) {
                    return
                }

                // if apply match is failure, dispose every query for match info
                if (event.isFailed) {
                    dispose()
                    return
                }

                if (event.callParams.isInvalid) {
                    mWaitStartTime = System.currentTimeMillis()
                    mMatchStatus = MATCH_STATUS_WAITING
                    waitingMatch()
                } else {
                    mMatchStatus = MATCH_STATUS_MATCHED
                    finishAndRemoveTask()
                    Router.instance.gotoVoiceMatchCallActivity(this@VoiceMatchWaitingActivity, event.callParams, true)
                }
            }

            @Subscribe(threadMode = ThreadMode.MAIN)
            fun onEvent(event: VoiceMatchCurrentRankEvent) {
                if (event.isNotFromThisRequestTag(requestTag)) {
                    return
                }
                rank_desc.text = getString(R.string.voice_match_waiting_rank_desc, event.result.toString())
                rank_desc.visibility = View.VISIBLE
            }
        })
    }

    private fun initData() {
        isShowing = true
        ImageViewUtil.loadAvatarRoundWithBoard(avatar, UserData.getInstance().user, 96, 2, R.color.user_info_avatar_border, false)
        Observable.timer(1, TimeUnit.SECONDS)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe {
                    apply()
                }
                .disposeOn(this)
        Observable.interval(0, 3, TimeUnit.SECONDS)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe {
                    mSubtitleIndex = (mSubtitleIndex + 1) % subtitleCount
                    val subtitleRes = if (UserData.getInstance().user.isMale) maleSubtitleMap[mSubtitleIndex] else femaleSubtitleMap[mSubtitleIndex]
                    subtitleRes?.let {
                        subtitle.setText(subtitleRes)
                    }
                }
                .disposeOn(this)
    }

    private fun waitingMatch() {
        checkIsInQueue()
        startQuitTimer()
        startRankTimer()
    }

    private fun checkIsInQueue() {
        mIsInQueueDisposable = Observable.interval(30, 30, TimeUnit.SECONDS)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe {
                    VoiceMatchData.isInQueue(requestTag)
                }
    }

    private fun startQuitTimer() {
        Observable.timer(5, TimeUnit.MINUTES)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe {
                    exit("timeout")
                    GlobalUI.getInstance().showToast(R.string.voice_match_waiting_timeout_toast)
                }
                .disposeOn(this)
    }

    private fun startRankTimer() {
        mRankDisposable = Observable.interval(0, 5, TimeUnit.SECONDS)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe {
                    VoiceMatchData.currentRank(requestTag)
                }
    }

    private fun apply() {
        VoiceMatchLogger.requestStart()
        VoiceMatchData.applyMatch(requestTag)
    }

    private fun exit(reason: String) {
        logEventForExitRequestStart(reason)
        VoiceMatchData.exitMatch(requestTag)
    }

    private fun logEventForExitRequestStart(reason: String) {
        val waitTime = (if (mWaitStartTime == 0L) 0L else System.currentTimeMillis() - mWaitStartTime).div(1000)
        VoiceMatchLogger.exitRequestStart(waitTime, reason)
    }

    private fun processCall(params: CallParams) {
        val powerManager = AppContext.getContext().getSystemService(Context.POWER_SERVICE) as PowerManager
        if (powerManager.isInteractive && AppActivityLifecycleCallbacks.getInstance().isAppInForeground) {
            Router.instance.gotoVoiceMatchCallActivity(this, params, false)
        } else {
            VoiceMatchNotificationUtils.instance.showVoiceMatchNotification(params, isCaller = false, isReceive = true)
        }
    }

    private fun playRingtone() {
        if (mRingtoneMediaPlayer != null && mRingtoneMediaPlayer?.isPlaying == true) {
            return
        }
        try {
            mRingtoneMediaPlayer = MediaPlayer.create(AppContext.getContext(), R.raw.voice_match_waiting_raw)
            mRingtoneMediaPlayer?.isLooping = true
            mRingtoneMediaPlayer?.start()
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    private fun closeRingtone() {
        mRingtoneMediaPlayer?.pause()
        mRingtoneMediaPlayer?.stop()
        mRingtoneMediaPlayer?.reset()
        mRingtoneMediaPlayer?.release()
        mRingtoneMediaPlayer?.setOnPreparedListener(null)
        mRingtoneMediaPlayer = null
    }

    override fun onDismiss() {
        finishAndRemoveTask()
    }

    private fun dispose() {
        mRankDisposable?.dispose()
        mIsInQueueDisposable?.dispose()
    }
}